import { throttle } from 'lodash';
import { useEffect, useMemo, useRef, useState, createContext, useContext } from 'react';
import { computedPrevNextColSpan } from './virtualAntd.less';

interface IParams {
  scrollY: number;
  scrollX?: number;
  rowHeight: number;

  // 横向所需
  colWidths?: number[];
  maxFixedLeftIndex?: number;
  onlyHorizotal?: boolean;
}

interface InnerParams extends IParams {
  colSpanParam: {
    curTableWith: number;
    colWidths: number[];
    maxFixedLeftIndex: number;
  };
  enableHorizontal: boolean;
}

const defaultParams = {
  scrollY: 500,
  rowHeight: 48,
};

const defaultInnerParams = {
  curTableWith: 100,
  colWidths: [48, 341],
  maxFixedLeftIndex: 1,
};

export function VList(VParams: Partial<IParams> = defaultParams) {
  // 固定的列最大的索引，便于计算横向
  const _params = {
    ...defaultParams,
    ...VParams,
    colSpanParam: {
      curTableWith: VParams.scrollX || defaultInnerParams.curTableWith,
      colWidths: VParams.colWidths || defaultInnerParams.colWidths,
      maxFixedLeftIndex: VParams.maxFixedLeftIndex || defaultInnerParams.maxFixedLeftIndex,
    },
    enableHorizontal: Boolean(VParams.scrollX && VParams.colWidths?.length),
  };

  return {
    body: {
      wrapper: createVWrapper(_params),
      row: createVRow(_params),
    },
  };
}

const ContextScrollLeft = createContext('');

/**
 * 维护一个循环 key 数组，滚动时将最上面的一条 tr 挪到最下面。
 * @param params
 * @returns JSX.Element
 */
const createVWrapper = (params: InnerParams) => (props: any, refOrigin: any) => {
  const { scrollY, rowHeight, colSpanParam, enableHorizontal, onlyHorizotal } = params;
  const { children, ...restProps } = props;

  const refInner = useRef(null);
  const refTbody = refOrigin?.current ? refOrigin : refInner;
  const [scrollTop, setScrollTop] = useState(0);
  const [scrollLeftStr, setScrollLeftStr] = useState(() =>
    computedPrevNextColSpan({
      ...colSpanParam,
      curScrollLeft: 0,
    }).join(','),
  );

  const offsetStart = scrollTop % rowHeight;
  const totalLen = children[1]?.length ?? 0;
  const renderLen = useMemo(() => Math.ceil(scrollY / rowHeight) + 1, []);
  const startIndex = Math.floor(scrollTop / rowHeight);
  const loopKeyArr = useMemo(
    () =>
      Array(totalLen)
        .fill(null)
        .map((_v, i) => i % renderLen),
    [totalLen, renderLen],
  );

  useEffect(() => {
    const handleScroll = throttle((e: { target: { scrollTop: any; scrollLeft: any } }) => {
      const curScrollTop = e.target?.scrollTop;
      const curScrollLeft = e.target?.scrollLeft;

      if (!onlyHorizotal) {
        let hasChangeTop = false;
        setScrollTop(prevScrollTop => {
          const scrollTopChange = Math.floor(curScrollTop / rowHeight) !== Math.floor(prevScrollTop / rowHeight);
          if (scrollTopChange) {
            window.console.time('hanldeScrollTop==>');
            hasChangeTop = true;
            return curScrollTop;
          }
          return prevScrollTop;
        });
        hasChangeTop && window.console.timeEnd('hanldeScrollTop==>');
      }

      if (enableHorizontal) {
        let hasChangeLeft = false;
        setScrollLeftStr(prevScrollLeftStr => {
          const curScrollLeftStr = computedPrevNextColSpan({
            ...colSpanParam,
            curScrollLeft,
          }).join(',');
          if (curScrollLeftStr !== prevScrollLeftStr) {
            window.console.time('hanldeScrollLeft==>');
            hasChangeLeft = true;
            return curScrollLeftStr;
          }
          return prevScrollLeftStr;
        });
        hasChangeLeft && window.console.timeEnd('hanldeScrollLeft==>');
      }
    }, 60);
    refTbody.current?.parentElement?.parentElement?.addEventListener('scroll', handleScroll);
    return () => {
      refTbody.current?.parentElement?.parentElement?.removeEventListener('scroll', handleScroll);
    };
  }, []);

  // 滑到最后可能会少一行
  const _startIndex = Math.min(startIndex, totalLen - renderLen);
  const computedChildren = useMemo(() => {
    if (!Array.isArray(children[1]) || onlyHorizotal) {
      return children;
    }
    return [
      children[0],
      children[1].slice(_startIndex, _startIndex + (renderLen || 1)),
      // .map((v: any, i: number) => ({ ...v, key: loopKeyArr[_startIndex + i] })),
    ];
  }, [children, _startIndex, renderLen, totalLen, loopKeyArr]);

  const totalHeight = totalLen * rowHeight;
  const scrolledRowHeight = scrollTop - offsetStart;
  const topHolderHeight = Math.min(scrolledRowHeight, totalHeight - renderLen * rowHeight);

  return (
    <ContextScrollLeft.Provider value={scrollLeftStr}>
      <tbody
        {...restProps}
        ref={refTbody}
        style={{
          ...restProps.style,
          height: totalHeight,
          transform: `translate3d(0, 0, 1px)`,
          position: 'relative',
          zIndex: 4,
        }}>
        {!onlyHorizotal ? (
          <tr
            key="topHolder"
            style={{
              opacity: 0,
              visibility: 'hidden',
              willChange: 'height',
              height: topHolderHeight,
            }}></tr>
        ) : null}
        {computedChildren}
        {!onlyHorizotal ? (
          <tr
            key="bottomHolder"
            style={{
              opacity: 0,
              visibility: 'hidden',
              willChange: 'height',
              height: (totalLen - renderLen) * rowHeight - topHolderHeight,
            }}></tr>
        ) : null}
      </tbody>
    </ContextScrollLeft.Provider>
  );
};

const createVRow = (_params: InnerParams) => (props: any) => {
  const { colSpanParam, enableHorizontal } = _params;
  const { children, ...restProps } = props;

  const scrollLeftStr = useContext(ContextScrollLeft);
  const { maxFixedLeftIndex } = colSpanParam;

  window.console.log('ttttr=>>');

  // 计算横向应该显示的 td
  const computedChildren = useMemo(() => {
    if (!Array.isArray(children) || !enableHorizontal) {
      return children;
    }
    const [prevHolderColSpan, nextHolderColSpan, prevBufferIndex, nextBufferIndex] = scrollLeftStr
      .split(',')
      .map(v => Number(v));
    return [
      ...children.slice(0, maxFixedLeftIndex + 1),
      prevHolderColSpan && <td colSpan={prevHolderColSpan} key="prevHolderTd" />,
      ...children.slice(prevBufferIndex, nextBufferIndex + 1),
      nextHolderColSpan && <td colSpan={nextHolderColSpan} key="nextHolderTd" />,
    ].filter(Boolean);
  }, [children, maxFixedLeftIndex, scrollLeftStr]);

  return <tr {...restProps}>{computedChildren}</tr>;
};
